"""
主控制器，负责协调视图和模型
"""
import os
import sys
import openblt
import threading
from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal
import time
from app.model.bootloader import BootloaderModel
from app.ui.main_window import MainWindow
from app.model.config_manager import ConfigManager

class MainController(QObject):
    """主控制器类，负责协调视图和模型"""
    
    progress_signal = pyqtSignal(int)
    log_signal = pyqtSignal(str)
    status_signal = pyqtSignal(str)
    operation_completed_signal = pyqtSignal(bool, str)
    
    def __init__(self, model : BootloaderModel, view : MainWindow):
        super().__init__()
        self.model = model
        self.view = view
        self._worker_thread = None
        self._stop_requested = False
        self.config_manager = ConfigManager()
        self.connect_signals()
        self.load_config()
    
    def connect_signals(self):
        """连接信号和槽"""
        # 视图到控制器的信号
        self.view.firmware_selected_signal.connect(self.on_firmware_selected)
        self.view.settings_changed_signal.connect(self.on_settings_changed)
        self.view.start_update_signal.connect(self.on_start_update)
        self.view.refresh_device_list_signal.connect(self.refresh_device_list)
        
        # 控制器信号到UI
        self.progress_signal.connect(self.view.update_progress)
        self.log_signal.connect(self.view.append_log)
        self.status_signal.connect(self.view.show_status_message)
        self.operation_completed_signal.connect(self.on_operation_completed)
        
        # 保留模型信号到UI（如有需要）
        self.model.status_updated.connect(self.view.append_log)
        self.model.operation_completed.connect(self.on_operation_completed)
        
        # 窗口关闭时保存配置
        self.view.aboutToClose.connect(self.save_config)
    
    @pyqtSlot(str)
    def on_firmware_selected(self, file_path):
        """处理固件文件选择"""
        self.model.set_firmware_file(file_path)
        # 启用更新按钮（如果有的话）
    
    @pyqtSlot(dict)
    def on_settings_changed(self, settings):
        """处理设置更改"""
        # 检查是否是通信接口设置
        if 'interface' in settings:
            self.model.update_settings(settings)
            self._update_interface_description()
        # 检查是否是会话协议设置
        elif 'protocol' in settings:
            # 更新模型中的会话协议设置
            if 'timeouts' in settings:
                self.model.settings['timeouts'] = settings['timeouts']
            if 'mode' in settings:
                self.model.settings['mode'] = settings['mode']
            if 'seed_key' in settings:
                self.model.settings['seed_key_file'] = settings['seed_key']
    
    @pyqtSlot()
    def on_start_update(self):
        """开始固件更新过程"""
        # 收集通讯参数并同步到model
        comm_settings = self.view.get_current_comm_settings()
        self.model.update_settings(comm_settings)
        # 禁用UI控件
        self.view.setEnabled(False)
        self._stop_requested = False
        self._worker_thread = threading.Thread(target=self.firmware_update_worker)
        self._worker_thread.daemon = True
        self._worker_thread.start()
    
    def firmware_update_worker(self):
        try:
            self.firmware_update_state_machine()
        except Exception as e:
            self.status_signal.emit(f"固件升级异常: {str(e)}")
            self.operation_completed_signal.emit(False, f"固件升级异常: {str(e)}")
        finally:
            self.status_signal.emit("升级线程结束")
            self.view.setEnabled(True)
    
    def firmware_update_state_machine(self):
        # 1. 初始化
        self.status_signal.emit("初始化固件升级引擎...")
        self.log_signal.emit("【初始化】开始初始化固件升级引擎...")
        if not self.model.initialize():
            self.log_signal.emit("【初始化】OpenBLT初始化失败")
            self.operation_completed_signal.emit(False, "OpenBLT初始化失败")
            self.view.setEnabled(True)
            return
        self.log_signal.emit("【初始化】OpenBLT初始化成功")

        # 2. 加载固件
        self.status_signal.emit("加载固件文件...")
        self.log_signal.emit("【加载固件】开始加载固件文件...")
        if not self.model.load_firmware():
            self.log_signal.emit("【加载固件】固件文件加载失败")
            self.operation_completed_signal.emit(False, "固件文件加载失败")
            self.view.setEnabled(True)
            return
        self.log_signal.emit("【加载固件】固件文件加载成功")

        # 3. 连接目标
        self.status_signal.emit("连接目标设备...")
        self.log_signal.emit("【连接】开始连接目标设备...")
        connect_result = self.model.connect()
        if not connect_result:
            self.log_signal.emit("【连接】连接目标设备失败或超时，已恢复操作权限")
            self.operation_completed_signal.emit(False, "连接目标设备失败或超时")
            self.view.setEnabled(True)
            return
        self.log_signal.emit("【连接】连接目标设备成功")

        # 4. 擦除（每个segment整体擦除，进度分配20%）
        segment_count = self.model.get_segment_count()
        if segment_count == 0:
            self.log_signal.emit("【擦除】固件文件中没有有效段")
            self.operation_completed_signal.emit(False, "固件文件中没有有效段")
            self.view.setEnabled(True)
            return
        total_bytes = 0
        for idx in range(segment_count):
            _, _, segment_len = self.model.get_segment(idx)
            total_bytes += segment_len
        erased_bytes = 0
        self.status_signal.emit("擦除目标存储器...")
        self.log_signal.emit("【擦除】开始擦除目标存储器...")
        for idx in range(segment_count):
            segment_data, segment_addr, segment_len = self.model.get_segment(idx)
            self.log_signal.emit(f"擦除 {segment_len} 字节，起始地址: 0x{segment_addr:X}")
            suc = self.model.erase_chunk(segment_addr, segment_len)
            if suc != openblt.BLT_RESULT_OK:
                self.log_signal.emit(f"【擦除】地址:0x{segment_addr:X}, 长度:0x{segment_len:X}, 擦除失败")
                self.operation_completed_signal.emit(False, f"地址:0x{segment_addr:X}, 长度:0x{segment_len:X}, 擦除失败")
                self.view.setEnabled(True)
                return
            erased_bytes += segment_len
            progress = int((erased_bytes / total_bytes) * 20)
            self.progress_signal.emit(progress)
            time.sleep(0.01)
        self.log_signal.emit("【擦除】目标存储器擦除完成")

        # 5. 写入（256字节分块，进度分配80%）
        self.status_signal.emit("写入固件...")
        self.log_signal.emit("【写入】开始写入固件...")
        written_bytes = 0
        chunk_size = 256
        for idx in range(segment_count):
            segment_data, segment_addr, segment_len = self.model.get_segment(idx)
            offset = 0
            while offset < segment_len:
                write_len = min(chunk_size, segment_len - offset)
                chunk = segment_data[offset:offset+write_len]
                addr = segment_addr + offset
                suc = self.model.write_chunk(addr, chunk)
                if suc != openblt.BLT_RESULT_OK:
                    self.status_signal.emit(f"写入失败: addr=0x{addr:X}, len={write_len}")
                    self.log_signal.emit(f"【写入】写入失败: addr=0x{addr:X}, len={write_len}")
                    self.operation_completed_signal.emit(False, f"写入失败: addr=0x{addr:X}, len={write_len}")
                    self.view.setEnabled(True)
                    return
                written_bytes += write_len
                progress = 20 + int((written_bytes / total_bytes) * 80)
                self.progress_signal.emit(progress)
                self.log_signal.emit(f"Programming {write_len} bytes at 0x{addr:X}")
                time.sleep(0.01)
                offset += write_len
        self.log_signal.emit("【写入】固件写入完成")

        # 6. 收尾
        self.status_signal.emit("收尾...")
        self.log_signal.emit("【断开】断开与目标设备的连接")
        self.model.disconnect()
        self.log_signal.emit("【升级完成】固件升级成功")
        self.status_signal.emit("固件升级完成")
        self.operation_completed_signal.emit(True, "固件升级成功")
    
    @pyqtSlot(bool, str)
    def on_operation_completed(self, success, message):
        """处理操作完成事件"""
        # 重新启用UI控件
        self.view.setEnabled(True)
        
        # 显示操作结果
        if success:
            self.view.show_status_message(f"成功: {message}")
        else:
            self.view.show_status_message(f"失败: {message}")
            
    def initialize(self):
        """初始化应用程序"""
        # 初始化模型
        self.model.initialize()
        
    def terminate(self):
        """终止应用程序"""
        self._stop_requested = True
        if self._worker_thread and self._worker_thread.is_alive():
            self._worker_thread.join(timeout=2)
        self.model.terminate()
        # 不在这里保存配置，改为在窗口关闭前保存

    def refresh_device_list(self):
        """刷新Device下拉框内容"""
        port_list = self.model.get_available_ports()
        self.view.set_device_list(port_list)

    def load_config(self):
        """加载配置文件并初始化UI和模型"""
        config = self.config_manager.load_config()
        if not config:
            return
        
        # 初始化会话参数
        if 'session' in config and 'xcp' in config['session']:
            xcp_config = config['session']['xcp']
            timeouts = {
                't1': xcp_config.get('timeout_t1', 1000),
                't3': xcp_config.get('timeout_t3', 2000),
                't4': xcp_config.get('timeout_t4', 10000),
                't5': xcp_config.get('timeout_t5', 1000),
                't6': xcp_config.get('timeout_t6', 50),
                't7': xcp_config.get('timeout_t7', 2000)
            }
            self.model.settings['timeouts'] = timeouts
            self.model.settings['mode'] = xcp_config.get('connect_mode', 0)
            self.model.settings['seed_key_file'] = xcp_config.get('seed_key', '')
            
            # 更新会话协议UI
            self.view.protocol_combo.setCurrentText("XCP version 1.0")  # 目前只支持XCP 1.0
            self.view.mode_combo.setCurrentText(str(xcp_config.get('connect_mode', 0)))
            self.view.seed_key_edit.setText(xcp_config.get('seed_key', ''))
            self.view.t1_spinbox.setValue(xcp_config.get('timeout_t1', 1000))
            self.view.t3_spinbox.setValue(xcp_config.get('timeout_t3', 2000))
            self.view.t4_spinbox.setValue(xcp_config.get('timeout_t4', 10000))
            self.view.t5_spinbox.setValue(xcp_config.get('timeout_t5', 1000))
            self.view.t6_spinbox.setValue(xcp_config.get('timeout_t6', 50))
            self.view.t7_spinbox.setValue(xcp_config.get('timeout_t7', 2000))
        
        # 初始化传输层参数和UI
        if 'transport' in config:
            transport_type = config['transport'].get('type', 'xcp_mbrtu')
            # 根据传输类型选择对应的接口
            interface_index = 0  # 默认为RS232
            if transport_type == 'xcp_rs232':
                interface_index = 0  # RS232
            elif transport_type == 'xcp_can':
                interface_index = 1  # CAN
            elif transport_type == 'xcp_usb':
                interface_index = 2  # USB
            elif transport_type == 'xcp_net':
                interface_index = 3  # TCP/IP
            elif transport_type == 'xcp_mbrtu':
                interface_index = 4  # Modbus RTU
            
            # 设置接口选择下拉框
            self.view.interface_combo.setCurrentIndex(interface_index)
            
            # 根据接口类型初始化参数
            if transport_type == 'xcp_rs232' and 'rs232' in config['transport']:
                rs232_config = config['transport']['rs232']
                # 设置RS232 UI
                self.view.rs232_device_combo.setCurrentText(rs232_config.get('device', 'COM1'))
                self.view.rs232_baudrate_combo.setCurrentText(str(rs232_config.get('baudrate', 57600)))
                self.view.rs232_checksum_combo.setCurrentIndex(rs232_config.get('cstype', 0))
            
            elif transport_type == 'xcp_can' and 'can' in config['transport']:
                can_config = config['transport']['can']
                # 设置CAN UI
                self.view.can_device_combo.setCurrentText(can_config.get('device', 'peak_pcanusb'))
                self.view.can_channel_combo.setCurrentText(str(can_config.get('channel', 0)))
                # 找到匹配的波特率选项
                baudrate_str = f"{can_config.get('baudrate', 500000) // 1000} kBit/sec"
                for i in range(self.view.can_baudrate_combo.count()):
                    if self.view.can_baudrate_combo.itemText(i) == baudrate_str:
                        self.view.can_baudrate_combo.setCurrentIndex(i)
                        break
                self.view.can_transmit_edit.setText(str(hex(can_config.get('transmit_id', 1639)))[2:])
                self.view.can_receive_edit.setText(str(hex(can_config.get('receive_id', 2017)))[2:])
                self.view.can_extended_checkbox.setChecked(bool(can_config.get('extended_id', 0)))
            
            elif transport_type == 'xcp_usb' and 'usb' in config['transport']:
                usb_config = config['transport']['usb']
                # 设置USB UI
                self.view.usb_vendor_edit.setText(usb_config.get('vendor_id', '1D50'))
                self.view.usb_product_edit.setText(usb_config.get('product_id', '60AC'))
            
            elif transport_type == 'xcp_net' and 'tcpip' in config['transport']:
                tcpip_config = config['transport']['tcpip']
                # 设置TCP/IP UI
                self.view.tcpip_addr_edit.setText(tcpip_config.get('address', '192.168.178.23'))
                self.view.tcpip_port_edit.setText(str(tcpip_config.get('port', 1000)))
            
            elif transport_type == 'xcp_mbrtu' and 'mbrtu' in config['transport']:
                mbrtu_config = config['transport']['mbrtu']
                # 设置Modbus RTU UI
                self.view.modbus_device_combo.setCurrentText(mbrtu_config.get('device', 'COM3'))
                self.view.modbus_baudrate_combo.setCurrentText(str(mbrtu_config.get('baudrate', 115200)))
                self.view.modbus_parity_combo.setCurrentIndex(mbrtu_config.get('parity', 0))
                self.view.modbus_stopbits_combo.setCurrentText(str(mbrtu_config.get('stopbits', 1)))
                self.view.modbus_destination_combo.setCurrentText(str(mbrtu_config.get('destination_address', 2)))
        
        # 刷新设备列表
        self.refresh_device_list()
        
        # 更新界面描述
        self._update_interface_description()

    def _update_interface_description(self):
        """更新界面描述，确保显示的通信接口信息与当前选择的接口一致"""
        try:
            selected_interface_text = self.view.interface_combo.currentText()
            self.view.firmware_update_desc_label.setText(f"固件升级使用 {selected_interface_text}")
        except Exception as e:
            print(f"更新界面描述出错: {str(e)}")
            
    def save_config(self):
        """保存当前配置到文件"""
        config = self.config_manager.get_default_config()
        
        # 安全检查：确保view对象仍然有效
        if hasattr(self, 'view') and self.view is not None and not getattr(self.view, '_is_deleted', False):
            try:
                # 获取当前窗口尺寸
                config['window'] = {
                    'width': self.view.width(),
                    'height': self.view.height()
                }
                
                # 获取传输层参数
                comm_settings = self.view.get_current_comm_settings()
                interface = comm_settings.get('interface', 'XCP on Modbus RTU')
                
                if interface == 'XCP on RS232':
                    transport_type = 'xcp_rs232'
                    config['transport'] = {
                        'type': transport_type,
                        'rs232': {
                            'device': comm_settings.get('device', 'COM1'),
                            'baudrate': comm_settings.get('baudrate', 57600),
                            'cstype': comm_settings.get('checksum', 0)
                        }
                    }
                elif interface == 'XCP on CAN':
                    transport_type = 'xcp_can'
                    config['transport'] = {
                        'type': transport_type,
                        'can': {
                            'device': comm_settings.get('device', 'peak_pcanusb'),
                            'channel': comm_settings.get('channel', 0),
                            'baudrate': comm_settings.get('baudrate', 500000),
                            'transmit_id': int(comm_settings.get('transmit', '667'), 16),
                            'receive_id': int(comm_settings.get('receive', '7E1'), 16),
                            'extended_id': 1 if comm_settings.get('extended', False) else 0
                        }
                    }
                elif interface == 'XCP on USB':
                    transport_type = 'xcp_usb'
                    config['transport'] = {
                        'type': transport_type,
                        'usb': {
                            'vendor_id': comm_settings.get('vendor_id', '1D50'),
                            'product_id': comm_settings.get('product_id', '60AC')
                        }
                    }
                elif interface == 'XCP on TCP/IP':
                    transport_type = 'xcp_net'
                    config['transport'] = {
                        'type': transport_type,
                        'tcpip': {
                            'address': comm_settings.get('address', '192.168.178.23'),
                            'port': int(comm_settings.get('port', '1000'))
                        }
                    }
                elif interface == 'XCP on Modbus RTU':
                    transport_type = 'xcp_mbrtu'
                    config['transport'] = {
                        'type': transport_type,
                        'mbrtu': {
                            'device': comm_settings.get('device', 'COM3'),
                            'baudrate': comm_settings.get('baudrate', 115200),
                            'parity': comm_settings.get('parity', 0),
                            'stopbits': comm_settings.get('stopbits', 1),
                            'destination_address': comm_settings.get('destination', 2)
                        }
                    }
                
                # 获取会话协议参数
                config['session'] = {
                    'type': 'xcp',
                    'xcp': {
                        'seed_key': self.view.seed_key_edit.text(),
                        'timeout_t1': self.view.t1_spinbox.value(),
                        'timeout_t3': self.view.t3_spinbox.value(),
                        'timeout_t4': self.view.t4_spinbox.value(),
                        'timeout_t5': self.view.t5_spinbox.value(),
                        'timeout_t6': self.view.t6_spinbox.value(),
                        'timeout_t7': self.view.t7_spinbox.value(),
                        'connect_mode': int(self.view.mode_combo.currentText())
                    }
                }
                
            except Exception as e:
                print(f"获取UI配置出错: {str(e)}")
        
        # 保存配置
        self.config_manager.save_config(config) 